package com.whjfxx.terminal.utils;

import lombok.SneakyThrows;
import net.sf.cglib.beans.BeanCopier;
import net.sf.cglib.beans.BeanMap;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.*;
import java.util.stream.Collector;
import java.util.stream.Collectors;

/**
 * Bean操作工具类
 * 
 * @author xiangtao
 * @Description:
 * @date 2019-05-27
 */
public class BeanUtils {

    /**
     * copy指定的字段
     * @param <T>
     * @param source
     * @param target
     * @param fieldName
     * @return
     */
    public static <T> T copyPropertiesInclude(Object source, T target, String... fieldName) {
        BeanMap sourceBeanMap = BeanMap.create(source);
        BeanMap targetBeanMap = BeanMap.create(target);
        for (String field : fieldName) {
            targetBeanMap.put(field, sourceBeanMap.get(field));
        }
        return target;
    }

    /**
     * copy时，排除指定的字段
     * @param <T>
     * @param source
     * @param target
     * @param excludeFieldName
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T copyPropertiesExclude(Object source, T target, String... excludeFieldName) {
        BeanMap sourceBeanMap = BeanMap.create(source);
        BeanMap targetBeanMap = BeanMap.create(target);
        sourceBeanMap.keySet().forEach(key -> {
            boolean flag = false;
            for (String filed : excludeFieldName) {
                if (filed.equals(key)) {
                    flag = true;
                    break;
                }
            }
            if (!flag) {
                if (targetBeanMap.containsKey(key)) {
                    targetBeanMap.put(key, sourceBeanMap.get(key));
                }
            }
        });
        return target;
    }

    public static void copyProperties(Object source, Object target) {
        BeanCopier copier = BeanCopier.create(source.getClass(), target.getClass(), false);
        copier.copy(source, target, null);
    }

    public static <S, T> T copyProperties(S source, Class<T> targetClass, BiConsumer<S, T> biConsumer) {
        BeanCopier copier = BeanCopier.create(source.getClass(), targetClass, false);
        try {
            T target = targetClass.getDeclaredConstructor().newInstance();
            copier.copy(source, target, null);
            if (biConsumer != null) {
                biConsumer.accept(source, target);
            }
            return target;
        } catch (Exception e) {
            if (e instanceof RuntimeException) {
                throw (RuntimeException) e;
            }
            throw new RuntimeException(e);
        }
    }

    public static <T> T copyProperties(Object source, Class<T> targetClass) {
        BeanCopier copier = BeanCopier.create(source.getClass(), targetClass, false);
        try {
            T target = targetClass.getDeclaredConstructor().newInstance();
            copier.copy(source, target, null);
            return target;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static <T> List<T> copyList(List<?> sourceList, Class<T> targetClass) {
        return copyList(sourceList, targetClass, null);
    }

    public static <S, T> List<T> copyList(List<S> sourceList, Class<T> targetClass, BiConsumer<S, T> biConsumer) {
        List<T> targetList = new ArrayList<T>();
        if (sourceList == null || sourceList.size() == 0) {
            return targetList;
        }
        sourceList.forEach(item -> {
            targetList.add(copyProperties(item, targetClass, biConsumer));
        });
        return targetList;
    }

    public static Map<String, Method> toMethodMap(Class<?> clazz) {
        Map<String, Method> map = new HashMap<>();
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            map.put(method.getName(), method);
        }
        return map;
    }

    @SuppressWarnings({"rawtypes"})
    public static void emptyToNull(Object obj) {
        if (obj == null) {
            return;
        }
        BeanMap map = BeanMap.create(obj);
        for (Object temp : map.entrySet()) {
            Map.Entry entry = (Map.Entry)temp;
            if (entry.getValue() != null && entry.getValue() instanceof String && "".equals(entry.getValue())) {
                map.put(entry.getKey(), null);
            }
        }
    }

    /**
     * 获取类中字段上的注解
     * @param <T>
     * @param <T>
     * @param clazz 类
     * @param fieldName 字段
     * @param annotationClass 注解类型
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T extends Annotation> T getFieldAnnotation(Class<?> clazz, String fieldName,
        Class<T> annotationClass) {
        Field field = ReflectionUtils.findField(clazz, fieldName);
        if (field != null) {
            Annotation annotation = field.getAnnotation(annotationClass);
            if (annotation != null) {
                return (T)annotation;
            }
        }
        return null;
    }

    /**
     * 判断指定的class中，是否存在指定的属性
     * @param clazz
     * @param fieldName
     * @return
     */
    public static boolean isExistField(Class<?> clazz, String fieldName) {
        Field field = ReflectionUtils.findField(clazz, fieldName);
        if (field != null) {
            return true;
        } else {
            return false;
        }
    }

    @SuppressWarnings("unchecked")
    public static <T extends Annotation> T getFieldAnnotation(Field field, Class<T> annotationClass) {
        if (field != null) {
            Annotation annotation = field.getAnnotation(annotationClass);
            if (annotation != null) {
                return (T)annotation;
            }
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    public static <K, V> Map<K, V> listToMap(String fieldName, List<V> list) {
        if (list == null || list.isEmpty()) {
            return Collections.emptyMap();
        }
        Map<K, V> map = new HashMap<>();
        for (V item : list) {
            BeanMap objMap = BeanMap.create(item);
            K key = (K)objMap.get(fieldName);
            if (Objects.nonNull(key)) {
                map.put(key, item);
            }
        }
        return map;
    }

    @SuppressWarnings("unchecked")
    public static <K, V, T> Map<K, V> listToMap(String keyFieldName, String valueFieldName, List<T> list) {
        if (list == null || list.isEmpty()) {
            return new HashMap<>();
        }
        Map<K, V> map = new HashMap<>();
        list.forEach(item -> {
            BeanMap objMap = BeanMap.create(item);
            K key = (K)objMap.get(keyFieldName);
            V value = (V)objMap.get(valueFieldName);
            Optional.ofNullable(key).ifPresent(k -> map.put(k, value));
        });
        return map;
    }

    /**
     * 获取列字段并且 去掉重复的数据
     * @param <T>
     * @param fieldName
     * @param list
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> List<T> getColumnValue(String fieldName, List<?> list) {
        List<T> columnList = new ArrayList<>();
        if (list == null || list.isEmpty()) {
            return columnList;
        }
        Class<?> clazz = list.get(0).getClass();
        if (!isExistField(clazz, fieldName)) {
            return columnList;
        }
        Set<T> set = new HashSet<>();
        list.forEach(item -> {
            BeanMap objMap = BeanMap.create(item);
            T obj = (T)objMap.get(fieldName);
            Optional.ofNullable(obj).ifPresent(v -> set.add(v));
        });
        columnList.addAll(set);
        return columnList;
    }

    /**
     * 从list中过滤出单个对象
     * @param collection
     * @param filter
     * @param <T>
     * @return
     */
    public static <T> T getOne(Collection<T> collection, Predicate<T> filter) {
        if (CollectionUtils.isEmpty(collection)) {
            return null;
        }
        return collection.stream().filter(filter).findFirst().orElse(null);
    }

    /**
     * 分组
     * @param collection
     * @param classifier
     * @param downstream
     * @param <T>
     * @param <K>
     * @param <A>
     * @param <D>
     * @return
     */
    public static <T, K, A, D> Map<K, D> groupingBy(Collection<T> collection, Function<T,K> classifier, Collector<T,A,D> downstream) {
        if (CollectionUtils.isEmpty(collection)) {
            return Collections.emptyMap();
        }
        return collection.stream().collect(Collectors.groupingBy(classifier,downstream));
    }

    /**
     * 分组
     * @param collection
     * @param classifier
     * @param <T>
     * @param <K>
     * @return
     */
    public static <T, K> Map<K, List<T>> groupingBy(Collection<T> collection, Function<T,K> classifier) {
        if (CollectionUtils.isEmpty(collection)) {
            return Collections.emptyMap();
        }
        return collection.stream().collect(Collectors.groupingBy(classifier));
    }

    /**
     * 取集合中的字段值为新的集合
     * @param collection
     * @param mapper
     * @param <T>
     * @param <R>
     * @return
     */
    public static <T,R> List<R> map(Collection<T> collection, Function<T,R> mapper) {
        if (null == collection || 0 == collection.size() || null == mapper) {
            return Collections.emptyList();
        }
        return collection.stream().map(mapper).collect(Collectors.toList());
    }

    /**
     * 取集合中的字段值为新的集合
     * @param collection
     * @param mapper
     * @param collectionFactory
     * @param <T>
     * @param <R>
     * @return
     */
    public static <T,R,C extends Collection<R>> C map(Collection<T> collection, Function<T,R> mapper, Supplier<C> collectionFactory) {
        if (null == collection || 0 == collection.size() || null == mapper || null == collectionFactory) {
            return collectionFactory.get();
        }
        return collection.stream().map(mapper).collect(Collectors.toCollection(collectionFactory));
    }

    /**
     * 从list中过滤部分数据
     * @param collection
     * @param filter
     * @param <T>
     * @return
     */
    public static <T> List<T> filterList(Collection<T> collection, Predicate<T> filter) {
        if (CollectionUtils.isEmpty(collection)) {
            return Collections.emptyList();
        }
        return collection.stream().filter(filter).collect(Collectors.toList());
    }

    public static <T> T mapToObject(Map<String, ?> map, Class<T> clazz) {
        try {
            final T newObj = clazz.newInstance();
            ReflectionUtils.doWithFields(clazz, (Field field) -> {
                try {
                    Object obj = map.get(field.getName());
                    if (Objects.nonNull(obj)) {
                        field.setAccessible(true);
                        if (!obj.getClass().isArray()) {
                            field.set(newObj, convertValType(obj, field.getType()));
                        } else if (((Object[]) obj).length > 0) {
                            if (!field.getType().isArray()) {
                                field.set(newObj, convertValType(((Object[]) obj)[0], field.getType()));
                            } else {
                                field.set(newObj, Arrays.asList(obj));
                            }
                        }
                    }
                } catch (Exception e) {}
            });
            return newObj;
        } catch (Exception e) {
            return null;
        }
    }

    private static Object convertValType(Object value, Class<?> fieldTypeClass) {
        if (Objects.isNull(value)) {
            return null;
        }
        DateFormat YYYY_MM_DD_MM_HH_SS = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        DateFormat YYYY_MM_DD = new SimpleDateFormat("yyyy-MM-dd");
        Object retVal = null;
        if (Long.class.getName().equals(fieldTypeClass.getName())
            || long.class.getName().equals(fieldTypeClass.getName())) {
            retVal = Long.parseLong(value.toString());
        } else if (Integer.class.getName().equals(fieldTypeClass.getName())
            || int.class.getName().equals(fieldTypeClass.getName())) {
            retVal = Integer.parseInt(value.toString());
        } else if (Float.class.getName().equals(fieldTypeClass.getName())
            || float.class.getName().equals(fieldTypeClass.getName())) {
            retVal = Float.parseFloat(value.toString());
        } else if (Double.class.getName().equals(fieldTypeClass.getName())
            || double.class.getName().equals(fieldTypeClass.getName())) {
            retVal = Double.parseDouble(value.toString());
        } else if (Date.class.getName().equals(fieldTypeClass.getName())
            && value.getClass().getName().equals(String.class.getName())) {
            if (value != null && value.toString().trim().length() > 0) {
                try {
                    if (value.toString().length() > 10) {
                        retVal = YYYY_MM_DD_MM_HH_SS.parse(value.toString());
                    } else {
                        retVal = YYYY_MM_DD.parse(value.toString());
                    }
                } catch (Exception e) {
                }
            }
        } else if (BigDecimal.class.getName().equals(fieldTypeClass.getName())) {
            retVal = new BigDecimal(value.toString());
        } else {
            retVal = value;
        }
        return retVal;
    }

    /**
     * 将bean 转换成 map
     * @param bean
     * @return
     */
    @SuppressWarnings("rawtypes")
    public static Map<String, Object> bean2Map(Object bean) {
        if (Objects.isNull(bean)) {
            return null;
        }
        Map<String, Object> map = new HashMap<>();
        BeanMap sourceBeanMap = BeanMap.create(bean);
        for (Object temp : sourceBeanMap.entrySet()) {
            Map.Entry entry = (Map.Entry)temp;
            map.put(entry.getKey().toString(), entry.getValue());
        }
        return map;
    }

    /**
     * convert collection to array
     *
     * @param list
     * @param clazz
     * @param <E>
     * @return
     */
    public static <E> E[] toArray(List<E> list, Class<E> clazz) {
        return list.toArray((E[]) Array.newInstance(clazz, list.size()));
    }

    @FunctionalInterface
    public interface AfterFunction<T> {

        /**
         * 转化后置处理方法
         * @param src 源对象
         * @param target 转换后的对象
         */
        void after(Object src, T target) throws IllegalAccessException;

    }
    
    /**
     * 给pojo对象中的String类型做trim操作
     * @param obj
     */
    @SuppressWarnings("rawtypes")
    public static void trim(Object obj) {
        if (obj == null) {
            return;
        }
        BeanMap map = BeanMap.create(obj);
        for (Object temp : map.entrySet()) {
            Map.Entry entry = (Map.Entry)temp;
            if (entry.getValue() != null && entry.getValue() instanceof String) {
                map.put(entry.getKey(),  ((String)entry.getValue()).trim());
            }
        }
    }
    
    /**
     * 多字段创建Map对象
     * @param <V>
     * @param keys
     * @param list
     * @return
     */
    public static <V> Map<String, V> listToMap(String[] keys, List<V> list) {
        if (list == null || list.isEmpty()) {
            return new HashMap<>();
        }
        Map<String, V> map = new HashMap<>();
        list.forEach(item -> {
            String key = createKey(keys, item);
            if (key != null && key.length() > 0) {
                map.put(key.toString(), item);
            }
        });
        return map;
    }

    /**
     * 多字段生产Key
     * @param keys
     * @param obj
     * @return
     */
    public static String createKey(String[] keys, Object obj) {
        BeanMap objMap = BeanMap.create(obj);
        StringBuffer key = new StringBuffer();
        for (String k : keys) {
            if (Objects.isNull(obj)) {
                key = null;
                break;
            }
            key.append(objMap.get(k)).append("::");
        }
        if (key != null && key.length() > 0) {
            key.delete(key.length() - 2, key.length());
        }
        return key != null ? key.toString() : "";
    }

    /**
     * 给为空的字段添加默认值
     * @param target
     * @param defaultShow
     * @return
     */
    @SneakyThrows
    public static Object addDefaultShow(Object target, String defaultShow){
        Field[] fields = target.getClass().getDeclaredFields();//通过反射获取到该对象
        for (Field field : fields) {
            field.setAccessible(true);//当要读取的属性为私有时，要设置为true
            String filedType = field.getGenericType().toString();//得到属性类型
            if(filedType.equals("class java.lang.String"))
            {
                Object filedValue = field.get(target);
                if(filedValue==null||StringUtils.isBlank(filedValue.toString())){
                    field.set(target , defaultShow);//对其进行重新赋值
                }
            }
        }
        return target;
    }


    /**
     * 检验对象属性是否都为空
     *
     * @param obj
     * @return
     */
    public static boolean isFieldAllNull(Object obj) {
        try {
            for (Field f : obj.getClass().getDeclaredFields()) {
                f.setAccessible(true);
                if (f.get(obj) != null) {
                    return false;
                }
            }
        } catch (IllegalAccessException ignored) {
        }
        return true;
    }

    /**
     * 取第一个值
     */
    public static BinaryOperator OPERATOR_FIRST = (first, second) -> first;

    /**
     * 取第二个值
     */
    public static BinaryOperator OPERATOR_SECOND = (first, second) -> second ;
}
